package com.dmbjz.seckill.server;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.dmbjz.common.constant.ApiConstant;
import com.dmbjz.common.constant.RedisKeyConstant;
import com.dmbjz.common.exception.ParameterException;
import com.dmbjz.common.model.domain.ResultInfo;
import com.dmbjz.common.model.entity.SeckillVouchers;
import com.dmbjz.common.model.entity.VoucherOrders;
import com.dmbjz.common.model.vo.SingInDinerInfo;
import com.dmbjz.common.utils.AssertUtil;
import com.dmbjz.common.utils.ResultInfoUtil;
import com.dmbjz.seckill.dao.SeckillVouchersDao;
import com.dmbjz.seckill.dao.VoucherOrdersDao;
import com.dmbjz.seckill.model.RedisLock;
import com.netflix.discovery.converters.Auto;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.concurrent.TimeUnit;


/*秒杀业务逻辑层*/
@Service
@Transactional(rollbackFor = Exception.class)
public class SeckillServcice {

    @Autowired
    private SeckillVouchersDao seckillVouchersDao;
    @Autowired
    private VoucherOrdersDao voucherOrdersDao;
    @Autowired
    private RestTemplate restTemplate;

    @Value("${service.name.ms-oauth-service}")
    private String oauth2ServerName;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private DefaultRedisScript defaultRedisScript;

    @Autowired
    private RedisLock redisLock;//Redis重入锁方法

    @Autowired
    private RedissonClient redissonClient;//第三方Redis工具类

    /*添加抢购代金券活动*/
    public void addSeckillVoucher(SeckillVouchers seckillVouchers){

        /*非空校验*/
        AssertUtil.isTrue(seckillVouchers.getFkVoucherId()==null,"代金券ID不存在!");
        AssertUtil.isTrue(seckillVouchers.getAmount()<0,"代金券数量不能为小于0!");
        AssertUtil.isTrue(seckillVouchers.getStartTime()==null,"代金券抢购开始时间不能为空!");
        AssertUtil.isTrue(seckillVouchers.getEndTime()==null,"代金券抢购结束时间不能为空!");
        AssertUtil.isTrue(seckillVouchers.getStartTime().after(seckillVouchers.getEndTime()),"抢购开始时间不能晚于结束时间!");

        /*注释原始 Mysql保存代金券活动的流程*/
        //        查询是否已有当前代金券秒杀活动
        //        SeckillVouchers getHot = seckillVouchersDao.selectVouchers(seckillVouchers.getFkVoucherId());
        //        AssertUtil.isTrue(getHot!=null,"已有该代金券抢购活动!");
        //seckillVouchersDao.save(seckillVouchers);   //保存活动数据进入数据库


        /*---------------采用Redis实现----------------*/
        String key = RedisKeyConstant.seckill_vouchers.getKey() + seckillVouchers.getFkVoucherId(); //组装Key
        Map <String,Object> map = redisTemplate.opsForHash().entries(key);                          //判断当前代金券是否开启活动
        AssertUtil.isTrue(!map.isEmpty() && (int)map.get("amount")>0,"该代金券已有抢购活动");

        seckillVouchers.setIsValid(1);
        seckillVouchers.setId( (int)( UUID.randomUUID().hashCode()+(Math.random()*1000)+new Random().nextInt(100000) ));//生成随机ID
        seckillVouchers.setCreateTime(new Date());
        seckillVouchers.setUpdateTime(new Date());
        Map<String, Object> changeMap = BeanUtil.beanToMap(seckillVouchers);
        redisTemplate.opsForHash().putAll(key,changeMap);


    }


    /*代金券抢购服务*/
    public ResultInfo doSeckill(Integer voucherId,String accesToken,String path){

        /*基本参数校验*/
        AssertUtil.isTrue(voucherId==null || voucherId<0,"请选择需要抢购的代金券");
        AssertUtil.isNotEmpty(accesToken,"请登录");



        /*注释原始 Mysql抢购代金券流程*/
        //        /*判断代金券是否有抢购活动*/
        //        SeckillVouchers seckillVouchers = seckillVouchersDao.selectVouchers(voucherId);
        //        AssertUtil.isTrue(seckillVouchers==null,"该代金券没有抢购活动!");
        //        /*判断活动是否有效*/
        //        AssertUtil.isTrue(seckillVouchers.getIsValid()==0,"该活动已经过期!");

        /*---------------采用Redis实现，使用Hutool将Map转为Bean----------------*/
        String key = RedisKeyConstant.seckill_vouchers.getKey() + voucherId;
        Map<String,Object> map = redisTemplate.opsForHash().entries(key);
        AssertUtil.isTrue(map.isEmpty(),"该代金券没有抢购活动");
        SeckillVouchers seckillVouchers = BeanUtil.mapToBean(map, SeckillVouchers.class, true, null);



        /*判断是否开始、结束*/
        Date now = new Date();
        AssertUtil.isTrue(seckillVouchers.getStartTime().after(now),"抢购活动还未开始!");
        AssertUtil.isTrue(seckillVouchers.getEndTime().before(now),"抢购活动已结束!");
        /*判断是否售空*/
        AssertUtil.isTrue(seckillVouchers.getAmount()<1,"该代金券已经卖完了!");


        /*获取登录用户信息*/
        String url = oauth2ServerName+"user/me?access_token={accesToken}";
        ResultInfo resultInfo = restTemplate.getForObject(url,ResultInfo.class,accesToken);
        if(resultInfo.getCode()!= ApiConstant.SUCCESS_CODE){
            resultInfo.setPath(path);
            return resultInfo;
        }
        /*这里的Data是一个LinkEdHashMap，SignInDinerInfo*/
        SingInDinerInfo dinerInfo = BeanUtil.fillBeanWithMap( (LinkedHashMap)resultInfo.getData(),new SingInDinerInfo(),false );
        /*判断该用户是否已经抢到*/
        VoucherOrders orders = voucherOrdersDao.findDinerOrder(dinerInfo.getId(),seckillVouchers.getId());
        AssertUtil.isTrue(orders!=null,"该用户已拥有代金券!");



        /*注释原始 Mysql减少库存流程*/
        //        /*减少库存*/
        //        int count = seckillVouchersDao.stockDecrease(seckillVouchers.getId());
        //        AssertUtil.isTrue(count==0,"代金券修改失败!");


        /*创建Redis重入锁，锁一个账号，解决一人多单问题*/
        String lockName = RedisKeyConstant.lock_key.getKey() + dinerInfo.getId() + ":" + voucherId;//登录用户的主键+优惠券ID组合为Key
        long expireTime = seckillVouchers.getEndTime().getTime() - now.getTime();

        /*使用自定义Redis分布锁*/
        //String lockKey = redisLock.tryLock(lockName, expireTime);

        RLock lock = redissonClient.getLock(lockName);

        try {

            /*自定义Redis分布式锁的判断*/
            //   if(StrUtil.isNotBlank(lockKey)){

            boolean isLocked = lock.tryLock(expireTime, TimeUnit.MICROSECONDS);
            if(isLocked){
                /*---------------采用Redis,添加LUA----------------*/
                List<String> keys = new ArrayList<>();
                keys.add(key);
                keys.add("amount");
                Long redisAccount = (Long)redisTemplate.execute(defaultRedisScript,keys);
                AssertUtil.isTrue(redisAccount==null || redisAccount <1,"该代金券剩余数量已空!");

                /*下单*/
                VoucherOrders voucherOrders = new VoucherOrders();
                voucherOrders.setFkDinerId(dinerInfo.getId());
                voucherOrders.setFkSeckillId(seckillVouchers.getId());
                voucherOrders.setFkVoucherId(seckillVouchers.getFkVoucherId());
                String orderNo = IdUtil.getSnowflake(1,1).nextIdStr();
                voucherOrders.setOrderNo(orderNo);
                voucherOrders.setOrderType(1);
                voucherOrders.setStatus(0);
                int save = voucherOrdersDao.save(voucherOrders);
                AssertUtil.isTrue(save==0,"用户抢购失败!");
            }

        } catch (Exception e) {
            /*手动回滚事务*/
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

            /*自定义Redis解锁*/
            //  redisLock.unlock(lockName,lockKey);

            lock.unlock();
            if(e instanceof ParameterException){
                return ResultInfoUtil.buildError(0,"卷已经卖完了!",path);
            }
        }

        return ResultInfoUtil.buildSuccess(path,"抢购成功!");

    }


}
